﻿using Sunny.UI;
using System.Drawing.Printing;
using System.Drawing.Text;

namespace envG
{
    public partial class Form打印报表 : UIForm
    {
        private DataGridView Dgv { get; set; }
        private bool IsLoadingDone { get; set; } = false;

        private Tuple<string, float>[] FontSizes { get; set; } = {
                new Tuple<string, float>("一", 26),
                new Tuple<string, float>("小一", 24),
                new Tuple<string, float>("二", 22),
                new Tuple<string, float>("小二", 18),
                new Tuple<string, float>("三", 16),
                new Tuple<string, float>("小三", 15),
                new Tuple<string, float>("四", 14),
                new Tuple<string, float>("小四", 12),
                new Tuple<string, float>("五", 10.5f),
                new Tuple<string, float>("小五", 9),
                new Tuple<string, float>("六", 7.5f),
                new Tuple<string, float>("小六", 6.5f),
                new Tuple<string, float>("七", 5.5f),
                new Tuple<string, Single>("八", 5),
            };
        private float ColumnMargin { get; set; } = 8;
        private int PrintCuser { get; set; } = 0;
        private int PaperMargin { get; set; } = 36;
        private Font PrintFont { get; set; } = new Font("宋体", 12);
        private float FontSize { get; set; } = 12;
        private string FontName { get; set; } = "宋体";
        private bool IsPrintTitle { get; set; } = true;
        private float PrintFontHeight { get; set; } = 2;
        private float RowSpace { get; set; } = 2 * 0.2f;
        private bool IsPreview { get; set; } = false;
        private int MaxRow { get; set; } = 0;

        public Form打印报表(in DataGridView gdv)
        {
            Dgv = gdv;
            InitializeComponent();
        }

        private void FrmPrinter_Load(object sender, EventArgs e)
        {
            if (Dgv == null)
            {
                MessageBox.Show("需要打印的对象为空，无法打印。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                DialogResult = DialogResult.Cancel;
            }

            if (!LoadingPrint())
            {
                MessageBox.Show("找不到可用的打印机，请安装打印机后再试。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                DialogResult = DialogResult.Cancel;
            }

            LoadingFont();
            IsLoadingDone = true;
            RefreshView();
        }

        /// <summary>
        /// 查找打印机
        /// </summary>
        /// <returns></returns>
        private bool LoadingPrint()
        {
            PrinterSettings ps = new PrinterSettings();
            foreach (string p in PrinterSettings.InstalledPrinters)
            {
                ps.PrinterName = p;
                CbPrinter.Items.Add(p);
                if (ps.IsDefaultPrinter)
                {
                    CbPrinter.SelectedItem = p;
                }
            }
            return CbPrinter.Items.Count > 0;
        }

        /// <summary>
        /// 初始化字体、字号
        /// </summary>
        void LoadingFont()
        {
            InstalledFontCollection fc = new InstalledFontCollection();
            foreach (FontFamily f in fc.Families)
            {
                CbFont.Items.Add(f.Name);
                if (f.Name == "宋体")
                {
                    CbFont.SelectedItem = f.Name;
                }
            }

            //初始化字号
            foreach (var fs in FontSizes)
            {
                CbFontSize.Items.Add(fs);
            }
            CbFontSize.SelectedIndex = 7;//对应小四 
        }

        /// <summary>
        /// 更换打印机，刷新打印机支持纸张
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CbPrinter_SelectedIndexChanged(object sender, EventArgs e)
        {
            PrinterSettings ps = new PrinterSettings()
            {
                PrinterName = (string)CbPrinter.SelectedItem
            };
            PrintDoc.PrinterSettings = ps;
            PrinterSettings.PaperSizeCollection psc = ps.PaperSizes;
            CbPaperSize.Items.Clear();
            int page = 0;
            for (int i = 0; i < psc.Count - 1; i++)
            {
                CbPaperSize.Items.Add(psc[i].PaperName);
                if (psc[i].PaperName == "A4")//默认为A4
                {
                    page = i;
                }
            }
            if (CbPaperSize.Items.Count > 1)
            {
                CbPaperSize.SelectedIndex = page;
            }

            RefreshView();
        }

        /// <summary>
        /// 纸张方向改变
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RbtnLandscape_CheckedChanged(object sender, EventArgs e)
        {
            PrintDoc.DefaultPageSettings.Landscape = RbtnLandscape.Checked;
            RefreshView();
        }

        /// <summary>
        /// 打印数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PrintDoc_PrintPage(object sender, PrintPageEventArgs e)
        {
            Graphics g = e.Graphics;
            SizeF sizeF;
            int line = 0;
            int flag = 1;
            ColumnMargin = PrintFont.GetHeight(g) * 2;
            lock (this.Dgv)
            {
                List<ExString> exs = new List<ExString>();
                for (int i = 0; i < Dgv.RowCount; i++)
                {
                    if (PrintCuser < Dgv.RowCount)
                    {
                        exs.Clear();
                        if (IsPrintTitle) //新页打印表头
                        {
                            float x0 = PaperMargin;
                            float y0 = PaperMargin;
                            for (int j = 0; j < Dgv.ColumnCount; j++)
                            {
                                string header = Dgv.Columns[j].HeaderText;
                                sizeF = g.MeasureString(header, PrintFont);
                                if (header == "时间")
                                {
                                    sizeF = g.MeasureString(DateTime.Now.ToString(), PrintFont);
                                }
                                exs.Add(new ExString(header, sizeF.Width));
                            }
                            flag = DrawExstring(g, line, exs);
                            line += flag;
                            exs.Clear();
                            IsPrintTitle = false;
                        }
                        float x = PaperMargin;
                        float y = (RowSpace + PrintFontHeight) * (i + 1) + PaperMargin;
                        for (int j = 0; j < Dgv.ColumnCount; j++)
                        {
                            //string text = Dgv.Rows[PrintCuser].Cells[j].Value.ToString();
                            string text = Dgv.Rows[PrintCuser].Cells[j].FormattedValue.ToString();
                            string header = Dgv.Columns[j].HeaderText;
                            sizeF = g.MeasureString(header, PrintFont);
                            if (header == "时间")
                            {
                                sizeF = g.MeasureString(DateTime.Now.ToString(), PrintFont);
                            }
                            exs.Add(new ExString(text, sizeF.Width));
                        }
                        line += DrawExstring(g, line, exs);
                        PrintCuser += 1;
                        if ((line + flag) > MaxRow && PrintCuser < Dgv.RowCount)
                        {
                            IsPrintTitle = true;
                            e.HasMorePages = IsPreview;
                            line = 0;
                            return;
                        }
                    }
                    else
                    {
                        e.HasMorePages = false;
                        g.Dispose();
                        return;
                    }
                }
            }
        }

        private void DrawLine(in Graphics g, in int line)
        {
            float x0 = PaperMargin;
            float y0 = (line + 1) * (PrintFontHeight + RowSpace) + PaperMargin;
            float x1;
            float y1 = y0;
            if (PrintDoc.DefaultPageSettings.Landscape)
            {
                x1 = PrintDoc.DefaultPageSettings.PaperSize.Height - 2 * PaperMargin;
            }
            else
            {
                x1 = PrintDoc.DefaultPageSettings.PaperSize.Width - 2 * PaperMargin;
            }
            g.DrawLine(new Pen(Color.FromArgb(255, 200, 200, 200)), x0, y0, x1, y1);
        }

        /// <summary>
        /// 绘制横线和文字
        /// </summary>
        /// <param name="g"></param>
        /// <param name="line"></param>
        /// <param name="exs"></param>
        /// <returns>下次起头行数</returns>
        private int DrawExstring(in Graphics g, in int line, List<ExString> exs)
        {
            int row = 0;
            float x = PaperMargin;
            float maxWidth;
            if (PrintDoc.DefaultPageSettings.Landscape)
            {
                maxWidth = PrintDoc.DefaultPageSettings.PaperSize.Height - 2 * PaperMargin;
            }
            else
            {
                maxWidth = PrintDoc.DefaultPageSettings.PaperSize.Width - 2 * PaperMargin;
            }
            foreach (ExString ex in exs)
            {
                if ((x + ex.Width) > maxWidth)
                {
                    DrawLine(g, line + row);//画线
                    row += 1;//下一行
                    x = PaperMargin;//起头
                }
                g.DrawString(ex.Text, PrintFont, Brushes.Black, x, (line + row) * (PrintFontHeight + RowSpace) + RowSpace + PaperMargin);
                x += ex.Width + ColumnMargin;
            }
            DrawLine(g, line + row);
            return row + 1;
        }

        private void RefreshView()
        {
            if (!IsLoadingDone)
            {
                return;
            }
            if (PrintPreviewControl.Width * PrintPreviewControl.Height == 0)
            {
                return;
            }
            PrintFont = new Font(FontName, FontSize);
            PrintFontHeight = PrintFont.GetHeight();
            RowSpace = PrintFontHeight * 0.2f;
            PrintCuser = 0;
            IsPrintTitle = true;

            double zoom;
            PaperSize pSize = PrintDoc.DefaultPageSettings.PaperSize;
            if (PrintDoc.DefaultPageSettings.Landscape)
            {
                //预览缩放
                zoom = (double)(PrintPreviewControl.Width - PaperMargin) / pSize.Height;
                //计算一页多少行，不包括表头
                MaxRow = (int)((pSize.Width - 2 * PaperMargin) / (RowSpace + PrintFontHeight));
            }
            else
            {
                //预览缩放
                zoom = (double)(PrintPreviewControl.Width - PaperMargin) / pSize.Width;
                //计算一页多少行，不包括表头
                MaxRow = (int)((pSize.Height - 2 * PaperMargin) / (RowSpace + PrintFontHeight));
            }
            PrintPreviewControl.Zoom = zoom;
            PrintPreviewControl.Document = PrintDoc;
            LblTotalPages.Text = $"总共{Math.Ceiling((double)Dgv.RowCount / MaxRow)}页";
        }

        /// <summary>
        /// 更改打印纸张
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CbPaperSize_SelectedIndexChanged(object sender, EventArgs e)
        {
            PrinterSettings ps = new PrinterSettings()
            {
                PrinterName = CbPrinter.SelectedItem.ToString()
            };
            PaperSize size = new PaperSize();
            foreach (PaperSize sz in ps.PaperSizes)
            {
                if (sz.PaperName == CbPaperSize.SelectedItem.ToString())
                {
                    size = sz;
                    break;
                }
            }
            PrintDoc.DefaultPageSettings.PaperSize = size;
            RefreshView();
        }

        private void FrmPrinter_Resize(object sender, EventArgs e)
        {
            RefreshView();
        }

        private void CbFont_SelectedIndexChanged(object sender, EventArgs e)
        {
            FontName = CbFont.SelectedItem.ToString();
            RefreshView();
        }

        private void CbFontSize_SelectedIndexChanged(object sender, EventArgs e)
        {
            FontSize = FontSizes[CbFontSize.SelectedIndex].Item2;
            RefreshView();
        }

        private void BtnOk_Click(object sender, EventArgs e)
        {
            try
            {
                IsPrintTitle = true;
                IsPreview = true;
                PrintCuser = 0;
                PrintDoc.Print();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        private class ExString
        {
            public float Width { get; }
            public string Text { get; }

            public ExString(string text, float width)
            {
                Text = text;
                Width = width;
            }
        }
    }
}